﻿using MLSMooc.Attributes;
using MLSMooc.Helper;
using MLSMooc.Session;
using MLSMooc.Session.Implement;
using MLSMooc.Session.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;

namespace MLSMooc.Filters
{
    public class SPSAuthorizationFilter : IAuthorizationFilter
    {
        public static int flag = 0;

        public void OnAuthorization(AuthorizationContext filterContext)
        {
            string requestType = filterContext.HttpContext.Request.RequestType;
            ActionDescriptor descriptor = filterContext.ActionDescriptor;
            //areaNm进行非空取值
            string areaNm = filterContext.RouteData.DataTokens["area"]?.ToString().ToLower();
            string ctrlerNm = descriptor.ControllerDescriptor.ControllerName.Trim().ToLower();
            string actionNm = descriptor.ActionName.Trim().ToLower();

            #region 存到Session中，主要是为了给导航做指引
            SessionBaseOpt.SetSessionState(ConstantHelper.AREA, areaNm);
            SessionBaseOpt.SetSessionState(ConstantHelper.CTRLER, ctrlerNm);
            SessionBaseOpt.SetSessionState(ConstantHelper.ACTION, actionNm);
            #endregion

            //#region 上下线时间判断，下线后转到提示页面，给/login/problem留个口子
            //if (ConstantHelper.OFFLINE_TIME <= DateTime.Now.Hour && DateTime.Now.Hour < ConstantHelper.ONLINE_TIME && !(ctrlerNm.Equals("login") && actionNm.Equals("problem")))
            //{
            //    filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Service_Unavailable;
            //    filterContext.Result = AuthorizationResult(requestType, (int)HttpCode.Service_Unavailable, string.Empty);
            //    //直接return
            //    return;
            //}
            //#endregion

            #region 权限判断
            object actionAttr = descriptor.GetCustomAttributes(typeof(NoAuthorizationAttribute), true).FirstOrDefault();
            object ctrlerAttr = descriptor.ControllerDescriptor.GetCustomAttributes(typeof(NoAuthorizationAttribute), true).FirstOrDefault();
            //只判断没有被[NoAuthorizationAttribute]标记过的ctrl和action
            if (actionAttr == null && ctrlerAttr == null)
            {
                //获取用户
                SessionUser userSession = SessionUserService.GetInstance().DeSerializeDecrypt(SessionBaseOpt.GetSessionState<string>(ConstantHelper.USER, string.Empty));

                #region 入参校验
                //get的QueryString
                foreach (string queryName in filterContext.HttpContext.Request.QueryString)
                {
                    try
                    {
                        string queryValue = filterContext.HttpContext.Request.QueryString[queryName];
                        if (RegularHelper.MatchSqlInjection(queryValue))
                        {
                            AttackOutPrint(null, filterContext, userSession.Id);          //记录攻击日志
                            filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                            filterContext.Result = FilterAttackResult(requestType, $"疑似SqlInjection:{{queryName:{queryName},queryValue:{queryValue}}}");
                            return;
                        }
                        if (RegularHelper.MatchXSS(queryValue))
                        {
                            AttackOutPrint(null, filterContext, userSession.Id);          //记录攻击日志
                            filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                            filterContext.Result = FilterAttackResult(requestType, $"疑似XSS:{{queryName:{queryName},queryValue:{queryValue}}}");
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        AttackOutPrint(ex, filterContext, userSession.Id);                  //记录攻击日志
                        filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                        filterContext.Result = FilterAttackResult(requestType, $"疑似攻击:{ex.Message}");
                        return;
                    }
                }

                //post的Form
                foreach (string formName in filterContext.HttpContext.Request.Form)
                {
                    try
                    {
                        string formValue = filterContext.HttpContext.Request.Form[formName];
                        if (RegularHelper.MatchSqlInjection(formValue))
                        {
                            AttackOutPrint(null, filterContext, userSession.Id);          //记录攻击日志
                            filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                            filterContext.Result = FilterAttackResult(requestType, $"疑似SqlInjection:{{formName:{formName},formValue:{formValue}}}");
                            return;
                        }
                        if (RegularHelper.MatchXSS(formValue))
                        {
                            AttackOutPrint(null, filterContext, userSession.Id);          //记录攻击日志
                            filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                            filterContext.Result = FilterAttackResult(requestType, $"疑似XSS:{{formName:{formName},formValue:{formValue}}}");
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        AttackOutPrint(ex, filterContext, userSession.Id);                  //记录攻击日志
                        filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                        filterContext.Result = FilterAttackResult(requestType, $"疑似攻击:{ex.Message}");
                        return;
                    }
                }
                #endregion

                #region session和权限判断
                //if (userSession == null)
                //{
                //    filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Unauthorized;
                //    filterContext.Result = AuthorizationResult(requestType, (int)HttpCode.Unauthorized, $"登录状态丢失");
                //    return;
                //}
                ////当前role的权限没找到，area允许null值
                //if (SpRoleInterfaceDAO.GetInstance().GetByAuthority(userSession.RoleId.ToString(), areaNm, ctrlerNm, actionNm) == null)
                //{
                //    filterContext.HttpContext.Response.StatusCode = (int)HttpCode.Forbidden;
                //    filterContext.Result = AuthorizationResult(requestType, (int)HttpCode.Forbidden, $"接口无权限访问");
                //    return;
                //} 
                #endregion
            }
            #endregion
        }

        private ActionResult AuthorizationResult(string requestType, int httpCode, string description)
        {
            if (requestType.ToLower().Equals("get"))
                return new RedirectResult($"{ConstantHelper.VIRTUAL_PATH}/Login/Problem/{httpCode}?description={description}");

            return new ContentResult()
            {
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8,
                Content = SerializeHelper.SerializeJson(new { httpCode = Enum.GetName(typeof(HttpCode), httpCode), description })
            };
        }

        private ActionResult FilterAttackResult(string requestType, string description)
        {
            if (requestType.ToLower().Equals("get"))
                return new RedirectResult($"{ConstantHelper.VIRTUAL_PATH}/Login/Problem/{(int)HttpCode.Forbidden}?description={description}");

            return new ContentResult()
            {
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8,
                Content = SerializeHelper.SerializeJson(new { httpCode = (int)HttpCode.Forbidden, description })
            };
        }

        private void AttackOutPrint(Exception ex, AuthorizationContext filterContext, int userId = 0)
        {
            //在项目的文件夹下面的Log文件夹中写入新的日志信息，以文本的方式记录
            StringBuilder sb = new StringBuilder();
            if (ex != null)
            {
                sb.AppendLine("------------------Catch 导致不能写入数据库的异常-----------------");
                sb.AppendLine($"Message::::::::::{ex.Message}");
                sb.AppendLine($"Source::::::::::{ex.Source}");
                sb.AppendLine($"StackTrace::::::::::{ex.StackTrace}");
                sb.AppendLine($"DateTime::::::::::{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")}");
                sb.AppendLine();
            }

            sb.AppendLine("------------------AttackFilter拦截的的异常-----------------");
            sb.AppendLine($"UserId:::::::::{userId}");
            sb.AppendLine($"IP:::::::::{filterContext.RequestContext.HttpContext.Request.UserHostAddress}");
            sb.AppendLine($"Agent:::::::::{filterContext.RequestContext.HttpContext.Request.UserAgent}");
            sb.AppendLine($"Cotroller:::::::::{ filterContext.RouteData.Values["controller"]?.ToString()}");
            sb.AppendLine($"Action:::::::::{ filterContext.RouteData.Values["action"]?.ToString()}");
            sb.AppendLine($"DateTime::::::::::{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")}");


            List<KeyValuePair<string, string>> queryList = new List<KeyValuePair<string, string>>();
            foreach (string queryName in filterContext.HttpContext.Request.QueryString) queryList.Add(new KeyValuePair<string, string>(queryName, filterContext.HttpContext.Request.QueryString[queryName]));
            sb.AppendLine($"QueryList:::::::::{SerializeHelper.SerializeJson(queryList)}");

            List<KeyValuePair<string, string>> formList = new List<KeyValuePair<string, string>>();
            foreach (string formName in filterContext.HttpContext.Request.Form) formList.Add(new KeyValuePair<string, string>(formName, filterContext.HttpContext.Request.Form[formName]));
            sb.AppendLine($"FormList:::::::::{SerializeHelper.SerializeJson(formList)}");

            string folder = HttpContext.Current.Server.MapPath(@"~/AttackInfo");
            if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);

            string path = folder + "\\" + CommonHelper.GetRandomFileNm() + ".txt";
            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                StreamWriter sw = new StreamWriter(fs);
                sw.Write(sb.ToString());
                sw.Flush();
                sw.Close();
            }
        }
    }
}